package com.bss.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.faces.model.DataModel;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.andy.basics.MessageHandler;
import com.andy.basics.Service;
import com.bss.dao.AdmissionDao;
import com.bss.dao.AttendentDao;
import com.bss.dao.IdCardTypeDao;
import com.bss.dao.PatientDao;
import com.bss.model.Admission;
import com.bss.model.Attendent;
import com.bss.model.Bed;
import com.bss.model.BedBooking;
import com.bss.model.Building;
import com.bss.model.CurrentAttendent;
import com.bss.model.MaterialAllocation;
import com.bss.model.MaterialSet;
import com.bss.model.Passport;
import com.bss.model.Patient;
import com.bss.model.Room;
import com.bss.model.RoomBooking;
import com.bss.service.AdmissionService;
import com.bss.service.CurrentAttendentService;
import com.bss.service.IdGenerationService;
import com.bss.service.PatientService;
import com.bss.service.PaymentService;
import com.bss.service.RoomService;

@Controller
@Scope("request")
public class AdmissionController extends com.andy.basics.Controller<Admission> {

	static int count = 1;

	private static final Logger LOGGER = Logger.getLogger(AdmissionController.class.getName());

	private Date exitDate = new Date();

	@Autowired
	private AttendentDao attendentDao;

	@Autowired
	private IdCardTypeDao idCardTypeDao;

	@Autowired
	private AdmissionService admissionService;

	@Autowired
	private PatientService patientService;

	private CurrentAttendent currentAttendent = new CurrentAttendent();

	private Integer selectedPatientId = null;

	@Autowired
	private IdGenerationService idGenerationService;

	private Integer admissionId;

	@Autowired
	private PatientDao patientDao;

	@Autowired
	private AdmissionDao admissionDao;

	@Autowired
	private CurrentAttendentService currentAttendentService;

	@Autowired
	private RoomService roomService;

	@Autowired
	private PaymentService paymentService;

	// Lets initialize the model in this request
	private Admission admission = null;

	private Admission exitableAdmission = null;

	private List<Building> buildings;

	public AdmissionController() {

	}

	@Override
	public Admission create() {
		return new Admission();
	}

	@Override
	public Service<Admission> getService() {
		return this.admissionService;
	}

	public void setCurrentAttendent(final CurrentAttendent currentAttendent) {
		this.currentAttendent = currentAttendent;
	}

	public CurrentAttendent getCurrentAttendent() {
		return this.currentAttendent;
	}

	public DataModel getCurrentAttendentsData() {
		return this.admissionService.getData();
	}

	public void addAttendent() {

		AdmissionController.LOGGER.debug("Value " + (this.getCurrentAttendent().getAttendent().getName() == null));

		boolean error = !this.getValidator().validate(this.currentAttendent.getAttendent(), this.getMessageHandler());

		if ((this.getCurrentAttendent().getAttendent().getIdCardType() != null)
				&& "Passport".equalsIgnoreCase(this.getCurrentAttendent().getAttendent().getIdCardType().getName())) {
			error = !this.getValidator()
					.validate(this.getCurrentAttendent().getAttendent().getPassport(), this.getMessageHandler()) || error;

		} else if ((this.getCurrentAttendent().getAttendent().getIdCardType() != null)
				&& (this.getCurrentAttendent().getAttendent().getIdCardNumber() == null)) {
			this.getMessageHandler().addMessage("Id Card Number", "Id Card Number cannot be empty");
			error = true;
		}

		if (error) {
			AdmissionController.LOGGER.debug("Attendent is not valid !");
			this.setError(true);
			this.setErrors(null);
			return;
		}

		for (CurrentAttendent currentAttendent : this.admissionService.getCurrentAttendents()) {
			if (currentAttendent.getAttendent().getName().equals(this.getCurrentAttendent().getAttendent().getName())) {
				this.setError(true);
				this.getErrors().add("Attendent " + this.getCurrentAttendent().getAttendent().getName() + " is already present !");
				return;
			}
		}

		if (this.getModel().getPatient().getId() != 0) {
			List<Attendent> attendents = this.attendentDao.findAllForPatient(this.getModel().getPatient(), "name='"
					+ this.currentAttendent.getAttendent().getName() + "'");
			if ((attendents != null) && (attendents.size() > 0)) {
				Attendent attendent = attendents.get(0);
				attendent.setDateOfBirth(this.currentAttendent.getAttendent().getDateOfBirth());
				this.currentAttendent.setAttendent(attendent);
			}
		}

		this.admissionService.add(this.currentAttendent);
		this.currentAttendent = new CurrentAttendent();
		// this.setError(false);
	}

	public void deleteAttendent() {
		this.admissionService.deleteAttendent();
	}

	public List<Building> getBuildings() {
		if (this.buildings == null) {
			this.buildings = this.admissionService.getBuildings();
		}
		return this.buildings;
	}

	public void setSelectedPatientId(final Integer selectedPatientId) {
		this.selectedPatientId = selectedPatientId;
	}

	public Integer getSelectedPatientId() {
		return this.selectedPatientId;
	}

	public Patient getPatient() {
		return this.selectedPatientId == null ? null : this.patientDao.findById(this.selectedPatientId);
	}

	public List<MaterialSet> getMaterialSetList() {
		return this.admissionService.getMaterialSetList();
	}

	public List<MaterialAllocation> getMaterialAllocations() {
		return this.admissionService.getMaterialAllocations();
	}

	@Override
	protected boolean validate(final Admission model, final MessageHandler handler) {
		return this.getValidator().validate(this.getModel().getPatient(), handler);
	}

	public void validate() {
		this.getModel().getPatient().setPhoto(this.patientService.getImageData());

		this.setError(!this.getValidator().validate(this.getModel(), this.getMessageHandler()));
		this.setError(!this.getValidator().validate(this.getModel().getPatient(), this.getMessageHandler()) || this.isError());

		// if (this.getModel().getPatient().getPhoto() == null) {
		// this.getMessageHandler().addMessage("Photo",
		// "Photograph cannot be empty");
		// this.setError(true);
		// }

		boolean error = this.isError();

		if ((this.getModel().getPatient().getIdCardType() != null)
				&& "Passport".equalsIgnoreCase(this.getModel().getPatient().getIdCardType().getName())) {
			error = !this.getValidator().validate(this.getModel().getPatient().getPassport(), this.getMessageHandler()) || error;
		} else if (("" + this.getModel().getPatient().getIdCardNumber()).isEmpty()) {
			this.getMessageHandler().addMessage("Id Card Number", "Id Card Number cannot be empty");
			error = true;
		}

		if (error) {
			this.setError(true);
			this.setErrors(null);
			return;
		}

	}

	@Override
	public String cancel() {
		this.patientService.setImageData(null);
		this.admissionService.cancel();
		this.admission = null;
		this.setSecurityDeposit(0);
		this.setUtensilDeposit(0);
		return super.cancel();
	}

	public DataModel admissions(final Object patientName) {
		if (patientName == null) {
			return null;
		}
		return this.idGenerationService.getCurrentAdmissionData(patientName.toString());
	}

	public void selectAdmission() {
		Admission admission = (Admission) this.idGenerationService.getData().getRowData();
		this.exitableAdmission = null;
		this.admissionService.setExitableAdmission(admission);
	}

	public Admission getExitableAdmission() {
		if ((this.exitableAdmission == null) && (this.admissionService.getExitableAdmission() != null)) {
			this.exitableAdmission = this.paymentService.populateOnly(this.admissionService.getExitableAdmission(), this.getDate());

			this.admissionService.setDeposits(this.exitableAdmission);
		}
		return this.exitableAdmission;
	}

	@Override
	public String add() {
		this.validate();
		if (this.isError()) {
			return null;
		}
		Admission admission = this.getModel();
		Date dateOfAdmission = new Date();
		admission.setDateOfAdmission(dateOfAdmission);
		admission.setDateOfExit(null);
		admission.setDate(dateOfAdmission);
		admission.getPatient().setPhoto(this.patientService.getImageData());
		this.addRoomAndBedBookings(admission, dateOfAdmission);
		this.addCurrentAttendents(admission, dateOfAdmission);
		this.addMaterialAllocations(admission, dateOfAdmission);

		try {
			String result = super.add();

			this.setAdmissionId(admission.getId());
			this.patientService.setImageData(null);

			return result;
		} catch (Exception e) {
			this.setError(true);
			List<String> errors = new ArrayList<String>();
			errors.add(e.getMessage() + " => " + this.getUtils().normalizeHtml(this.getUtils().getRootCause(e).getMessage()));
			this.setErrors(errors);
			AdmissionController.LOGGER.error(e.getMessage(), e);
			e.printStackTrace();
		}
		return null;
	}

	protected void addMaterialAllocations(final Admission admission, final Date dateOfAdmission) {
		// After each call, it keeps using the same list
		admission.getMaterialAllocations().clear();
		for (MaterialAllocation materialAllocation : this.getMaterialAllocations()) {
			materialAllocation.setAdmission(admission);
			materialAllocation.setDateAllocated(dateOfAdmission);
			materialAllocation.setDateReturned(null);
			admission.getMaterialAllocations().add(materialAllocation);
		}
	}

	protected void addCurrentAttendents(final Admission admission, final Date dateOfAdmission) {
		admission.getCurrentAttendents().clear();
		for (CurrentAttendent currentAttendent : this.admissionService.getCurrentAttendents()) {
			currentAttendent.setAdmission(admission);
			currentAttendent.getAttendent().setPatient(admission.getPatient());
			currentAttendent.setDateOfArrival(dateOfAdmission);
			currentAttendent.setDateOfLeave(null);
			admission.getCurrentAttendents().add(currentAttendent);
		}
	}

	protected void addRoomAndBedBookings(final Admission admission, final Date dateOfAdmission) {
		admission.getBedBookings().clear();
		admission.getRoomBookings().clear();
		this.setSelectedAccomodation(admission, dateOfAdmission);
	}

	protected void setSelectedAccomodation(final Admission admission, final Date dateOfAdmission) {
		for (Building building : this.getBuildings()) {
			for (Room room : building.getRooms()) {
				if (!room.isDormitory()) {
					if (room.isSelected()) {
						RoomBooking roomBooking = new RoomBooking();
						roomBooking.setAdmission(admission);
						roomBooking.setRoom(room);
						roomBooking.setCheckInDate(dateOfAdmission);
						roomBooking.setCheckOutDate(null);
						roomBooking.setRate(room.getProposedRate());
						admission.getRoomBookings().add(roomBooking);
					}
				} else {
					for (Bed bed : room.getBeds()) {
						if (bed.isSelected()) {
							BedBooking bedBooking = new BedBooking();
							bedBooking.setAdmission(admission);
							bedBooking.setBed(bed);
							bedBooking.setCheckInDate(dateOfAdmission);
							bedBooking.setCheckOutDate(null);
							bedBooking.setRate(bed.getProposedRate());
							admission.getBedBookings().add(bedBooking);
						}
					}
				}
			}
		}
	}

	@Override
	public void reset() {
		this.admissionService.clearRoomAllocation();
		super.reset();
		this.admissionService.setCurrentAttendents(null);
		this.admissionService.setMaterialAllocations(null);
		this.admissionService.setCurrentAttendentsData(null);
		this.currentAttendent = new CurrentAttendent();

	}

	public DataModel patients(final Object patientName) {
		if (patientName == null) {
			return null;
		}
		return this.admissionService.getPatients(patientName.toString());
	}

	public DataModel attendents(final Object attendentName) {
		if ((attendentName == null) || (this.getModel().getPatient().getId() == 0)) {
			return null;
		}
		return this.currentAttendentService.getAttendentsData(this.getModel().getPatient(), attendentName.toString());
	}

	public DataModel getAttendents() {
		return this.admissionService.getAttendentsDataModel();
	}

	@Override
	public String select() {
		Patient patient = (Patient) this.admissionService.getPatients().getRowData();

		patient = this.patientDao.findById(patient.getId());
		this.getModel().setPatient(patient);

		// Lets set the last case number
		Admission lastAdmission = this.admissionDao.findLastAdmission(patient.getId());
		this.getModel().setCaseNo(lastAdmission.getCaseNo());
		this.getModel().setMedicalProblem(lastAdmission.getMedicalProblem());
		this.patientService.setImageData(patient.getPhoto());

		// This is done just to staisfy the UI
		if (patient.getPassport() == null) {
			patient.setPassport(new Passport());
		}
		this.getModel().setMaterialSet(new MaterialSet());

		this.admissionService.select(patient);

		return null;
	}

	public String selectAttendent() {
		Attendent attendent = (Attendent) this.currentAttendentService.getSelectableCurrentAttendent().getRowData();
		this.currentAttendent.setAttendent(attendent);
		return null;
	}

	public DataModel getExitedAdmissions() {
		return this.admissionService.getExitedAdmissions();
	}

	public void startCheckoutNew() {
		this.setError(false);
		this.setErrors(null);
		try {
			this.admissionService.startCheckoutNew(new Date());
		} catch (Exception e) {
			AdmissionController.LOGGER.error("Error while checking out " + e.getMessage(), e);
			this.generateError(e);
			e.printStackTrace();
		}
	}

	protected void generateError(Exception e) {
		List<String> errors = new ArrayList<String>();
		errors.add(e.getMessage());
		this.setErrors(errors);
		this.setError(true);
	}

	public void selectAttendents() {
		if (this.admissionService.getAttendents() != null) {
			for (Attendent a : this.admissionService.getAttendents()) {
				if (a.isSelected()) {
					this.currentAttendent = new CurrentAttendent();
					this.currentAttendent.setAttendent(a);
					this.addAttendent();
					this.currentAttendent = new CurrentAttendent();
				}
			}
		}
	}

	public void setAdmissionId(final Integer admissionId) {
		this.admissionId = admissionId;
	}

	public Integer getAdmissionId() {
		return this.admissionId;
	}

	public void action() {
		// this.admissionService.selectDate(this.exitDate);
	}

	public void selectRooms() {

		List<Integer> rooms = new ArrayList<Integer>();

		for (Building building : this.getBuildings()) {
			for (Room room : building.getRooms()) {
				if (!room.isDormitory()) {
					if (room.isSelected()) {
						rooms.add(room.getId());
					}
				}
			}
		}

		this.admissionService.setRooms(rooms);

	}

	public void selectBeds() {

		List<Integer> beds = new ArrayList<Integer>();

		for (Building building : this.getBuildings()) {
			for (Room room : building.getRooms()) {
				if (room.isDormitory()) {
					for (Bed bed : room.getBeds()) {
						if (bed.isSelected()) {
							beds.add(bed.getId());
						}
					}
				}
			}
		}

		this.admissionService.setBeds(beds);

	}

	public List<Room> findDormitories(final Building building, final int floor) {
		List<Room> rooms = new ArrayList<Room>();
		for (Building buildingVar : this.getBuildings()) {
			if (buildingVar.getId().equals(building.getId())) {
				for (Room room : buildingVar.getRooms()) {
					if (room.isDormitory() && room.getFloor().equals(floor)) {
						rooms.add(room);
					}
				}
			}
		}
		return rooms;
	}

	public List<Room> findRooms(List<Building> buildings) {
		List<Room> rooms = new ArrayList<Room>();
		for (Building building : buildings) {
			for (Room room : building.getRooms()) {
				rooms.add(room);
			}
		}
		return rooms;
	}

	public List<Room> findRooms() {
		return this.findRooms(this.getBuildings());
	}

	public List<Room> findRooms(final String condition) throws Exception {
		try {
			return this.roomService.findRooms(this.findRooms(), condition);
		} catch (Exception e) {
			AdmissionController.LOGGER.error("Exception in findRooms(): " + e.getMessage(), e);
		}
		return null;
	}

	public List<Room> findDormitories(final String condition) throws Exception {
		try {
			return this.roomService.findDormitories(this.findRooms(), condition);
		} catch (Exception e) {
			AdmissionController.LOGGER.error("Exception in findRooms(): " + e.getMessage(), e);
		}
		return null;
	}

	public List<Room> getSelectedRooms(List<Building> buildings) {
		List<Room> rooms = new ArrayList<Room>();
		for (Room room : this.findRooms(buildings)) {
			if (room.isSelected()) {
				rooms.add(room);
			}
		}
		return rooms;
	}

	public List<Bed> getSelectedBeds(List<Building> buildings) {
		List<Bed> beds = new ArrayList<Bed>();
		for (Room room : this.findRooms(buildings)) {
			for (Bed bed : room.getBeds()) {
				if (bed.isSelected()) {
					beds.add(bed);
				}
			}

		}
		return beds;
	}

	public boolean isPassportRelated() {
		Admission admission = this.getModel();
		if ((admission.getPatient().getCountry() == null) || (admission.getPatient().getCountry().getId() == 0)) {
			return false;
		}
		return !"India".equalsIgnoreCase(admission.getPatient().getCountry().getName());
	}

	public long getRandom() {
		return System.nanoTime();
	}

	public void selectCountry() {
		Admission admission = this.getModel();
		if (this.isPassportRelated()) {
			admission.getPatient().setIdCardType(this.idCardTypeDao.findOnly("name='Passport'"));
		} else {
			admission.getPatient().setIdCardType(null);
		}
		admission.getPatient().setState(null);
		admission.getPatient().setPlace(null);
	}

	public void selectState() {
		this.getModel().getPatient().setPlace(null);
	}

	@Override
	public Admission getModel() {
		if (this.admission == null) {
			this.admission = super.getModel();
		}
		return this.admission;
	}

	public String addNewPlace() {
		try {
			this.getHttpUtil().redirect("places.jsf");
		} catch (IOException e) {
			AdmissionController.LOGGER.error(e.getMessage(), e);
		}
		return null;
	}

	public int getSecurityDeposit() {
		return this.admissionService.getSecurityDeposit();
	}

	public void setSecurityDeposit(int deposit) {
		this.admissionService.setSecurityDeposit(deposit);
	}

	public int getUtensilDeposit() {
		return this.admissionService.getUtensilDeposit();
	}

	public void setUtensilDeposit(int deposit) {
		this.admissionService.setUtensilDeposit(deposit);
	}

	public void updateUtensilDeposit() {
		this.admissionService.setUtensilDeposit(this.getModel().getMaterialSet() == null ? 0 : this.getModel().getMaterialSet()
				.getDeposit());
	}

	public int getUtensilRefund() {
		return this.admissionService.getUtensilRefund();
	}

	public void setUtensilRefund(int utensilRefund) {
		this.admissionService.setUtensilRefund(utensilRefund);
	}

	public int getSecurityRefund() {
		return this.admissionService.getSecurityRefund();
	}

	public void setSecurityRefund(int securityRefund) {
		this.admissionService.setSecurityRefund(securityRefund);
	}

	public void setBuildings(List<Building> buildings) {
		this.buildings = buildings;
	}

}
